#include "mainwindow.h"
//#include "ui_mainwindow.h"
#include <QtSvg>
#include <QGraphicsScene>
#include <QGraphicsSimpleTextItem>
#include <QTextStream>
#include <QInputDialog>
#include <QMenu>
#include <QAction>
#include "player.h"

player * trick_winner(card * trick[], int num_cards_played=4)  //note non member
{
    card * high_card = trick[0];
    for (int i=1; i<num_cards_played; ++i)
    {
        if (trick[i] > high_card)
            high_card=trick[i];
    }
    high_card->owner->tricks++;
    return high_card->owner;
}


MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), state((STATES)0), out(stdout),
WINNING_SCORE(500), LOSING_SCORE(-200), NUM_HANDS(10), NIL_POINTS(100),
BAG_POINTS(1), MAX_BAGS(10), BAG_PENALTY(100), BOARD(4),
ALLOW_LYING(false), SPADES_MUST_BE_BROKEN(true),
ALLOW_SMALL_BLINDS(false), ALLOW_BLIND_NIL(false),
BIG_BONUS(true), BONUS_POINTS(100), TRICKS_FOR_BONUS(10)
//, ui(new Ui::MainWindow)
{
    //ui->setupUi(this);
    uint gamenum =QDateTime::currentDateTime().toTime_t();
    qsrand(gamenum);
    //red->setFlag( QGraphicsItem::ItemIsMovable );
    this->setWindowTitle("Gamenum: " + QString::number(gamenum) );
    qApp->setApplicationName("Spades (C) JPM");
    scene = new QGraphicsScene(this);
    view = new QGraphicsView(scene,this);
    card::RENDERER = new QSvgRenderer();
    setCentralWidget(view);

    this->doDeck();
    this->doPlayers();
    this->doScoreboard();
    this->doTrickbox();
    this->doMenu();
    this->resize(qApp->desktop()->availableGeometry().size());

    //QGraphicsEllipseItem *item = new QGraphicsEllipseItem( 0, scene );
    //item->setRect( 0, 0, 50.0, 50.0 );
}

void MainWindow::doMenu()
{

    QMenu * game = this->menuBar()->addMenu(tr("&Game"));
    QAction * newgame = new QAction(tr("&New Game"),this);
    game->addAction(newgame);
    connect(newgame, SIGNAL(triggered()), this,SLOT(doNewGame()));
    //menubar->setGeometry(QRect(0,0,275,22));
}

void MainWindow::doNewGame()
{
    //while( ! this->isWinner() )
    this->doDeal();
    this->redraw();
    qApp->processEvents();
    this->doBid();
    this->doPlay();
}

MainWindow::~MainWindow()
{
    card * c;
    while (deck.count())
    { c = deck.takeLast();
        delete c;
    }

    for (int x=0; x<4; x++)
    {   while (players[x]->hand.count())
        { card * c = players[x]->hand.takeLast();
            delete c;
        }
    }
    //delete ui;
}

/*
struct js { int a; int b;
    js(int aa, int bb) : a(aa), b(bb) {}
    js(const js & orig) : a(orig.a), b(orig.b) {}
};


bool operator<(js l, js r)
{
    if (l.a < r.a) return true;
    if (l.a > r.a) return false;
    else return (l.b < r.b);
}
*/

int binary_search(const QList<card *> & lst, card * item)
{   //static QTextStream out(stdout);
    int first=0, last=lst.count()-1;
    //out << "{F0L" << last << '}'; out.flush();
    while (first <= last) {
        int mid = (first + last) / 2;  // compute mid point.
        if ( *(lst[mid]) < *item ) //becareful with *derefrence, make sure you are comparing the object, and not the object addresses
        { first = mid + 1;  // repeat search in top half.
            // out << "[F" << first << ']'; out.flush();
        }
        else if ( *item < *(lst[mid]) )
        {
            last = mid - 1; // repeat search in bottom half.
            //  out << "[L" << last << "]"; out.flush();
        }
        else
            return mid;     // found it. return position /////
    }
    return first;    // failed to find key
}

void qlist_sorted_insert(QList<card *> & list, card * item)
{
    int idx=binary_search(list,item);
    list.insert(idx,item);
}

void MainWindow::resizeEvent(QResizeEvent *event)
{
    event->accept();
    this->redraw();
}

void MainWindow::redraw()
{
    scene->setSceneRect(0, 0, this->width(), this->height());
    double scale = qMin(width()/card::CARDSIZE.width(), height() / card::CARDSIZE.height()) / 4;

    player * thisplayer = players[0];
    for (int x=0; x < thisplayer->hand.count(); x++)
    {
        thisplayer->hand[x]->svg.setScale( scale );
        thisplayer->hand[x]->svg.setPos(width()/17*x + width()/17, height() - scale * card::CARDSIZE.height() );
        thisplayer->hand[x]->svg.setZValue(x);
        thisplayer->hand[x]->svg.setOpacity(100);
    }
    scoreboard->setScale(qMin((double)height()/250,(double)width()/250));
    out << qMin((double)height(),(double)width()) << endl;
}

#define printhand(p_index) for (int i=0; i<players[p_index]->hand.count(); ++i){ int val=players[p_index]->hand[i]->sv2num(); out << val << ' ';} out << "\n"; out.flush();

void MainWindow::doDeal()
{
    this->state = DEALING;
    static const int HAND_SIZE=13;
    for (int p_index=0; p_index<4; ++p_index) //player index
        for (int card_pick=0; card_pick<HAND_SIZE; ++card_pick)
        {
        int cardnum= qrand() % deck.count();
        card * c=deck.takeAt(cardnum);
        c->owner=players[p_index];
        qlist_sorted_insert(players[p_index]->hand, c);
    }
    /* printhand(0);
    printhand(1);
    printhand(2);
    printhand(3);
    */
    isSpadesBroken=false;
}


void MainWindow::doBid()
{   this->state = BIDDING;
    //int bidding_player;
    player * bidding_player=leader;

    int player_idx;
    NS_score->setText("");
    EW_score->setText("");
    for(player_idx=0; player_idx<4;++player_idx)
    {   players[player_idx]->score=0;
        players[player_idx]->tricks=0;
        players[player_idx]->isBiddingNil=false;
        players[player_idx]->hasClubs=true;
        players[player_idx]->hasDiamonds=true;
        players[player_idx]->hasHearts=true;
        players[player_idx]->hasSpades=true;
    }

    for(int cnt=0; cnt<4; ++cnt, bidding_player=bidding_player->left)
    {
        bidding_player->currentbid = ((*bidding_player).*((*bidding_player).bid))();
        //derefrencing member pointer to function -- I'm so sorry.
        if (bidding_player->isHuman)
            bidding_player->currentbid=doUiBid(bidding_player->currentbid); //filling default bid with suggestion

        QString sBid= bidding_player->name[0] + ": ";
        if (bidding_player->currentbid == 0)
            sBid+="Nil";
        else
            sBid+=QString::number(bidding_player->currentbid);

        if (cnt  <= 1) {
            bidding_player->txtBid->setText(sBid);
        }
        else {
            sBid = bidding_player->txtBid->text() + "\n" + sBid;
            bidding_player->txtBid->setText(sBid);
        }
    }
}

int MainWindow::doUiBid(int defaultbid)
{
    bool ok;
    return QInputDialog::getInt(this,"Bidding input dialog","Bid(Tricks): ", defaultbid, 0, 13,1,&ok);
}

bool MainWindow::isWinner()
{
    for (int p_idx=0; p_idx<4; ++p_idx)
    {
        if (players[p_idx]->score >= this->WINNING_SCORE || players[p_idx]->score <= this->LOSING_SCORE)
            return true;
    }
    return false;
}

void MainWindow::doDeck()
{
    QString DECK = QLatin1String("/Users/jmalacho/Documents/svn/qt4spades/deck.svg");
    card::RENDERER->load(DECK);
    for (int x=0; x<52; x++)
    {    this->deck.append(new card(x));
        this->scene->addItem(&(deck[x]->svg));
        this->deck[x]->svg.setPos(0,0);
        this->deck[x]->svg.setZValue(-1);
        this->deck[x]->svg.setOpacity(0);
    }
    card::CARDSIZE = deck[0]->svg.boundingRect().size();
}

void MainWindow::doPlayers()
{
    player * jon= new player("Jon");
    player * p2 = new player("Parnter",false);
    jon->setPartner(p2);


    player * e1= new player("Max",false);
    player * e2= new player("Lafs",false);
    e1->setPartner(e2);

    jon->setLeft(e1);
    e1->setLeft(p2);
    p2->setLeft(e2);
    e2->setLeft(jon);

    players[0] = jon;
    players[1] = e1;
    players[2] = p2;
    players[3] = e2;
    uint randomplayer=qrand()%4;
    players[randomplayer]->setDealer();
    leader = players[randomplayer]->left;
}

void MainWindow::doScoreboard()
{

    //begin scoreboard creation
    QList<QGraphicsItem *> temp;
    QFont name_font(QFont("Arial",8));

    QGraphicsSimpleTextItem * ns_name = scene->addSimpleText(players[0]->name + "\n" + players[0]->partner->name);
    ns_name->setFont( name_font );

    QGraphicsSimpleTextItem * ew_name = scene->addSimpleText(players[0]->left->name + "\n" + players[0]->right->name);
    ew_name->setFont( name_font );

    //QGraphicsSimpleTextItem * lbls = scene->addSimpleText("\n\nS\nB");
    //lbls->setFont( name_font );

    NS_score = scene->addSimpleText("0000");
    NS_score->setFont( name_font );

    EW_score = scene->addSimpleText("0000");
    EW_score->setFont( name_font );

    NS_bid = scene->addSimpleText("00");
    NS_bid->setFont( name_font );

    EW_bid = scene->addSimpleText("00");
    EW_bid->setFont( name_font );

    //calculate distances
    double score_row_h( qMax( EW_score->boundingRect().height(), NS_score->boundingRect().height() )),
    score_w( qMax( EW_score->boundingRect().width(), NS_score->boundingRect().width() ) ),
    //name_w(  qMax( ns_name->boundingRect().width(), ew_name->boundingRect().width())),
    name_row_h(  qMax( ns_name->boundingRect().height(), ew_name->boundingRect().height())),
    col_1_w(0),
    col_2_w( 1 + qMax(score_w, ns_name->boundingRect().width())),
    col_3_w( 1 + qMax(score_w, ew_name->boundingRect().width()));
    const double x_offset=1, y_offset=1;
    // PLACEMENT
    // N:  ns_name  ew_name
    // S: ns_score ew_score
    // B:   ns_bid   ew_bid

    ns_name->setPos(x_offset+col_1_w,           y_offset);
    ew_name->setPos(x_offset+col_1_w + col_2_w, y_offset);
    NS_score->setPos(x_offset+col_1_w,          y_offset + name_row_h);
    EW_score->setPos(x_offset+col_1_w + col_2_w,y_offset + name_row_h);
    NS_bid->setPos(x_offset + col_1_w,          y_offset + name_row_h+score_row_h);
    EW_bid->setPos(x_offset+col_1_w + col_2_w,  y_offset + name_row_h+score_row_h);

    QGraphicsRectItem * nw_scorebox = scene->addRect(x_offset+col_1_w,          y_offset + name_row_h,col_2_w,score_row_h);
    QGraphicsRectItem * ew_scorebox = scene->addRect(x_offset+col_1_w + col_2_w,y_offset + name_row_h,col_3_w,score_row_h);
    temp.clear(); temp << nw_scorebox << ew_scorebox << NS_score << EW_score << NS_bid << EW_bid
            << ns_name << ew_name;
    scoreboard=scene->createItemGroup( temp );

    players[0]->setScoreboard(NS_bid,NS_score);
    players[0]->left->setScoreboard(EW_bid, EW_score);
}


//positions for cards
void MainWindow::doTrickbox()
{
    N_trickbox = scene->addRect(width()/2, height()/3,card::CARDSIZE.width(), card::CARDSIZE.height());
    S_trickbox = scene->addRect(width()/2, height()*2/3,card::CARDSIZE.width(), card::CARDSIZE.height());
    W_trickbox = scene->addRect(width()/3, height()/2,card::CARDSIZE.width(), card::CARDSIZE.height());
    E_trickbox = scene->addRect(width()*2/3, height()/2,card::CARDSIZE.width(), card::CARDSIZE.height());
    QList<QGraphicsItem *> temp;
    temp<< N_trickbox << S_trickbox << W_trickbox << E_trickbox;
    trickbox = scene->createItemGroup(temp);
}

void MainWindow::doPlay() {
    for (int tricknum=0; tricknum<13; ++tricknum) {
        player * p=leader;
        for(int cnt=0; cnt<4; p=p->left) {
            if (p->isHuman)
                trick[cnt]=doUiPlay();
            else
                trick[cnt]=((*p).*((*p).play))(trick, cnt, discard);  //again derefrencing pointer to play function (card * return)
            //TODO: display cards
            cnt++;
        }
        leader = trick_winner(trick);
        update_bid_tracking();
        clean_up(trick);
        //TODO: display stacked cardbacks for visual on tricks one
        // when >= 6, stack for BOOK
    }
}

void MainWindow::clean_up(card * trick[]) {
    for (int cnt=0; cnt<4; ++cnt) {
        card * c = trick[cnt];
        if (c->suit == card::SPADE)
            this->isSpadesBroken=true;
        if (c->suit != trick[0]->suit)
        {
            switch (c->suit) {
            case card::CLUB:
                c->owner->hasClubs=false; break;
            case card::DIAMOND:
                c->owner->hasDiamonds=false; break;
            case card::HEART:
                c->owner->hasHearts=false; break;
            case card::SPADE:
                c->owner->hasSpades=false; break;
            }
        }
        qlist_sorted_insert(discard, c);
    }
}

void MainWindow::update_bid_tracking()
{
    player * p = players[0];
    while(! p->isDealer ) p=p->left;
    for (int cnt=0; cnt<4; p=p->left, ++cnt) {
        QString sBid=p->name[0] + ": (" + QString::number(p->tricks) + ")";
        if (p->currentbid == 0)
            sBid+="Nil";
        else
            sBid+=QString::number(p->currentbid);

        if (cnt  <= 1) {
            p->txtBid->setText(sBid);
        }
        else {
            sBid = p->txtBid->text() + "\n" + sBid;
            p->txtBid->setText(sBid);
        }
    }
}

void MainWindow::update_scoreboard(player * a, player * b) {
    //special scoring rules here.
    int score=0;
    if (a->isBiddingNil) {
        if (a->tricks == 0)
            score+= NIL_POINTS;
        else score-=NIL_POINTS;
    }

    if (b->isBiddingNil) {
        if (b->tricks == 0)
            score+=NIL_POINTS;
        else score-=NIL_POINTS;
    }

    int total_tricks = a->tricks + b->tricks;
    int total_bid    = a->currentbid + b->currentbid;
    if (total_tricks >= total_bid) {
        score += (10 * total_bid);
        score += (total_tricks - total_bid) * BAG_POINTS;
        if (BIG_BONUS && total_bid >= TRICKS_FOR_BONUS && total_tricks==total_bid )
            score += BONUS_POINTS;
    } else
    {
        score -= (10 * total_bid);
    }

    a->score+=score;

    if ( a->score%10 >= MAX_BAGS )
        a->score-= BAG_PENALTY;

    b->score=a->score;
    a->txtScore->setText(QString::number(a->score));
}


card * MainWindow::doUiPlay()
{

}
